<template>
  <div :class="['vc-hue', directionClass]">
    <div class="vc-hue-container"
         role="slider"
         :aria-valuenow="colors.hsl.h"
         aria-valuemin="0"
         aria-valuemax="360"
         ref="container"
         @mousedown="handleMouseDown"
         @touchmove="handleChange"
         @touchstart="handleChange">
      <div class="vc-hue-pointer" :style="{top: pointerTop, left: pointerLeft}" role="presentation">
        <div class="vc-hue-picker"></div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, reactive, Ref, ref, watch } from 'vue'

export default defineComponent({
  name: 'Hue',
  props: {
    colors: {
      type: Object,
      required: true,
    },
    direction: {
      type: String,
      // [horizontal | vertical]
      default: 'horizontal',
    },
  },
  emits: ['change'],
  setup(props, { emit }) {
    const container: Ref = ref(null)

    const hueData = reactive({
      oldHue: 0,
      pullDirection: '',
    })

    watch(() => props.colors, () => {
      const h = props.colors.hsl.h
      if (h !== 0 && h - hueData.oldHue > 0) hueData.pullDirection = 'right'
      if (h !== 0 && h - hueData.oldHue < 0) hueData.pullDirection = 'left'
      hueData.oldHue = h
    })

    const handleChange = (e: Event, skip = false) => {
      !skip && e.preventDefault()

      const containerElement = container.value
      if (!containerElement) {
        // for some edge cases, container may not exist. see #220
        return
      }
      const containerWidth = containerElement.clientWidth
      const containerHeight = containerElement.clientHeight

      const xOffset = containerElement.getBoundingClientRect().left + window.scrollX
      const yOffset = containerElement.getBoundingClientRect().top + window.scrollY
      const pageX = (e as any).pageX || ((e as any).touches ? (e as any).touches[0].pageX : 0)
      const pageY = (e as any).pageY || ((e as any).touches ? (e as any).touches[0].pageY : 0)
      const left = pageX - xOffset
      const top = pageY - yOffset

      let h
      let percent

      if (props.direction === 'vertical') {
        if (top < 0) {
          h = 360
        } else if (top > containerHeight) {
          h = 0
        } else {
          percent = -(top * 100 / containerHeight) + 100
          h = (360 * percent / 100)
        }

        if (props.colors.hsl.h !== h) {
          emit('change', {
            h: h,
            s: props.colors.hsl.s,
            l: props.colors.hsl.l,
            a: props.colors.hsl.a,
            source: 'hsl',
          })
        }
      } else {
        if (left < 0) {
          h = 0
        } else if (left > containerWidth) {
          h = 360
        } else {
          percent = left * 100 / containerWidth
          h = (360 * percent / 100)
        }

        if (props.colors.hsl.h !== h) {
          emit('change', {
            h: h,
            s: props.colors.hsl.s,
            l: props.colors.hsl.l,
            a: props.colors.hsl.a,
            source: 'hsl',
          })
        }
      }
    }

    const handleMouseDown = (e: Event) => {
      handleChange(e, true)
      window.addEventListener('mousemove', handleChange)
      window.addEventListener('mouseup', handleMouseUp)
    }
    const handleMouseUp = (e: Event) => {
      unbindEventListeners()
    }
    const unbindEventListeners = () => {
      window.removeEventListener('mousemove', handleChange)
      window.removeEventListener('mouseup', handleMouseUp)
    }


    return {
      container,
      directionClass: computed(() => {
        return {
          'vc-hue--horizontal': props.direction === 'horizontal',
          'vc-hue--vertical': props.direction === 'vertical',
        }
      }),
      pointerTop: computed(() => {
        if (props.direction === 'vertical') {
          if (props.colors.hsl.h === 0 && hueData.pullDirection === 'right') return 0
          return -((props.colors.hsl.h * 100) / 360) + 100 + '%'
        } else {
          return 0
        }
      }),
      pointerLeft: computed(() => {
        if (props.direction === 'vertical') {
          return 0
        } else {
          if (props.colors.hsl.h === 0 && hueData.pullDirection === 'right') return '100%'
          return (props.colors.hsl.h * 100) / 360 + '%'
        }
      }),
      handleChange,
      handleMouseDown,
    }

  },
})
</script>